/*FreeMind - A Program for creating and viewing Mindmaps
*Copyright (C) 2000-2006 Joerg Mueller, Daniel Polansky, Christian Foltin, Dimitri Polivaev and others.
*
*See COPYING for Details
*
*This program is free software; you can redistribute it and/or
*modify it under the terms of the GNU General Public License
*as published by the Free Software Foundation; either version 2
*of the License, or (at your option) any later version.
*
*This program is distributed in the hope that it will be useful,
*but WITHOUT ANY WARRANTY; without even the implied warranty of
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*GNU General Public License for more details.
*
*You should have received a copy of the GNU General Public License
*along with this program; if not, write to the Free Software
*Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
/*
 * Created on 16.03.2004
 *
 */
package accessories.plugins;

import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import accessories.plugins.dialogs.ChooseFormatPopupDialog;

import com.jgoodies.forms.builder.DefaultFormBuilder;

import freemind.common.PropertyBean;
import freemind.common.PropertyControl;
import freemind.common.SeparatorProperty;
import freemind.common.TextTranslator;
import freemind.common.XmlBindingTools;
import freemind.controller.Controller;
import freemind.controller.actions.generated.instance.Pattern;
import freemind.controller.actions.generated.instance.Patterns;
import freemind.extensions.HookRegistration;
import freemind.modes.MindMap;
import freemind.modes.MindMapNode;
import freemind.modes.ModeController;
import freemind.modes.StylePatternFactory;
import freemind.modes.mindmapmode.MindMapController;
import freemind.modes.mindmapmode.hooks.PermanentMindMapNodeHookAdapter;
import freemind.preferences.FreemindPropertyContributor;
import freemind.preferences.FreemindPropertyListener;
import freemind.preferences.layout.OptionPanel;

/**
 * @author foltin
 * 
 */
public class AutomaticLayout extends PermanentMindMapNodeHookAdapter {

    private static final String AUTOMATIC_FORMAT_LEVEL = "automaticFormat_level";

    /**
     * Registers the property pages.
     * 
     * @author foltin
     * 
     */
    public static class Registration implements HookRegistration {
        private AutomaticLayoutPropertyContributor mAutomaticLayoutPropertyContributor;

        private final MindMapController modeController;

        private static FreemindPropertyListener listener = null;

        public Registration(ModeController controller, MindMap map) {
            modeController = (MindMapController) controller;
        }

        static class MyFreemindPropertyListener implements FreemindPropertyListener{
            public void propertyChanged(String propertyName,
                    String newValue, String oldValue) {
                if (propertyName.startsWith(AUTOMATIC_FORMAT_LEVEL)) {
                    patterns = null;
                }
            }
        };
        public void register() {
            // add listener:
            if (listener == null) {
                listener = new MyFreemindPropertyListener();
            }
            Controller.addPropertyChangeListener(listener);

            mAutomaticLayoutPropertyContributor = new AutomaticLayoutPropertyContributor(
                    modeController);
            OptionPanel.addContributor(mAutomaticLayoutPropertyContributor);
        }

        public void deRegister() {
            OptionPanel.removeContributor(mAutomaticLayoutPropertyContributor);
            Controller.removePropertyChangeListener(listener);
        }

    }
    
    /**
     * Translates style pattern properties into strings. 
     * */
    static class StylePropertyTranslator implements TextTranslator {
        private final MindMapController controller;
        
        StylePropertyTranslator(MindMapController controller) {
            super();
            this.controller = controller;
        }
        
        public String getText(String pKey) {
            return controller.getText(pKey);
        }
    }
    

    /** Currently not used.
     *  Is useful if you want to make single patterns changeable.
     *  */
    public static class StylePatternProperty extends PropertyBean implements
            PropertyControl, ActionListener {

        String description;

        String label;

        String pattern;

        JButton mButton;

        private final TextTranslator mTranslator;

        private final MindMapController mindMapController;

        public StylePatternProperty(String description, String label,
                TextTranslator pTranslator, MindMapController pController) {
            super();
            this.description = description;
            this.label = label;
            mTranslator = pTranslator;
            mindMapController = pController;
            mButton = new JButton();
            mButton.addActionListener(this);
            pattern = null;
        }

        public String getDescription() {
            return description;
        }

        public String getLabel() {
            return label;
        }

        public void setValue(String value) {
            pattern = value;
            Pattern resultPattern = getPatternFromString();
            String patternString = StylePatternFactory.toString(resultPattern,
                    new StylePropertyTranslator(mindMapController));
            mButton.setText(patternString);
            mButton.setToolTipText(patternString);
        }

        public String getValue() {
            return pattern;
        }

        public void layout(DefaultFormBuilder builder,
                TextTranslator pTranslator) {
            JLabel label = builder.append(pTranslator.getText(getLabel()),
                    mButton);
            label.setToolTipText(pTranslator.getText(getDescription()));
            // add "reset to standard" popup:

        }

        public void actionPerformed(ActionEvent arg0) {
            // construct pattern:
            Pattern pat = getPatternFromString();
            ChooseFormatPopupDialog formatDialog = new ChooseFormatPopupDialog(
                    mindMapController.getFrame().getJFrame(),
                    mindMapController,
                    "accessories/plugins/AutomaticLayout.properties_StyleDialogTitle",
                    pat);
            formatDialog.setModal(true);
            formatDialog.setVisible(true);
            // process result:
            if (formatDialog.getResult() == ChooseFormatPopupDialog.OK) {
                Pattern resultPattern = formatDialog.getPattern();
                resultPattern.setName("dummy");
                pattern = XmlBindingTools.getInstance().marshall(resultPattern);
                setValue(pattern);
                firePropertyChangeEvent();
            }
        }

        private Pattern getPatternFromString() {
            return StylePatternFactory.getPatternFromString(pattern);
        }

        public void setEnabled(boolean pEnabled) {
            mButton.setEnabled(pEnabled);
        }

    }

    public static class StylePatternListProperty extends PropertyBean implements
            PropertyControl, ListSelectionListener {

        String description;

        String label;

        String patterns;

        JList mList;
        
        boolean mDialogIsShown=false;

        private final TextTranslator mTranslator;

        private final MindMapController mindMapController;

        private DefaultListModel mDefaultListModel;

        public StylePatternListProperty(String description, String label,
                TextTranslator pTranslator, MindMapController pController) {
            super();
            this.description = description;
            this.label = label;
            mTranslator = pTranslator;
            mindMapController = pController;
            mList = new JList();
            mList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
            mDefaultListModel = new DefaultListModel();
            mList.setModel(mDefaultListModel);
            mList.addListSelectionListener(this);
            patterns = null;
        }

        public String getDescription() {
            return description;
        }

        public String getLabel() {
            return label;
        }

        public void setValue(String value) {
            patterns = value;
            Patterns resultPatterns = getPatternsFromString();
            mDefaultListModel.clear();
            int j = 1;
            StylePropertyTranslator stylePropertyTranslator = new StylePropertyTranslator(mindMapController);
            for (Iterator i = resultPatterns.getListChoiceList().iterator(); i.hasNext();) {    
                Pattern pattern = (Pattern) i.next();
                mDefaultListModel.addElement(mTranslator.getText("level"+j) + ": " + StylePatternFactory.toString(pattern, stylePropertyTranslator));
                j++;
            }
        }

        public String getValue() {
            return patterns;
        }

        public void layout(DefaultFormBuilder builder,
                TextTranslator pTranslator) {
            JLabel label = builder.append(pTranslator.getText(getLabel()));
            builder.append(new JLabel());
            label.setToolTipText(pTranslator.getText(getDescription()));
            builder.appendSeparator();
            builder.append(new JScrollPane(mList), 3);
//            builder.append(mList);
        }


        private Patterns getPatternsFromString() {
            return StylePatternFactory.getPatternsFromString(patterns);
        }

        public void setEnabled(boolean pEnabled) {
            mList.setEnabled(pEnabled);
        }

        public void valueChanged(ListSelectionEvent e) {
            // construct pattern:
            final Patterns pat = getPatternsFromString();
            JList source = (JList)e.getSource();
            if(source.getSelectedIndex() < 0)
                return;
            final Pattern choice = (Pattern) pat.getChoice(source.getSelectedIndex());
            final ChooseFormatPopupDialog formatDialog = new ChooseFormatPopupDialog(
                    mindMapController.getFrame().getJFrame(),
                    mindMapController,
                    "accessories/plugins/AutomaticLayout.properties_StyleDialogTitle",
                    choice);
            // FIXME: What's that? (fc, 8,4,2008).
            EventQueue.invokeLater(new Runnable(){
				public void run() {
					if(mDialogIsShown)
						return;
					mDialogIsShown = true;
		            try {
						formatDialog.setModal(true);
						formatDialog.setVisible(true);
						// process result:
						if (formatDialog.getResult() == ChooseFormatPopupDialog.OK) {
							formatDialog.getPattern(choice);
							patterns = XmlBindingTools.getInstance().marshall(
									pat);
							setValue(patterns);
							firePropertyChangeEvent();
						}
					} finally {
						mDialogIsShown = false;
					}
				}
            	
            });
        }

    }

    private static final class AutomaticLayoutPropertyContributor implements
            FreemindPropertyContributor {

        private final MindMapController modeController;

        public AutomaticLayoutPropertyContributor(
                MindMapController modeController) {
            this.modeController = modeController;
        }

        public List getControls(TextTranslator pTextTranslator) {
            Vector controls = new Vector();
            controls
                    .add(new OptionPanel.NewTabProperty(
                            "accessories/plugins/AutomaticLayout.properties_PatternTabName"));
            controls
                    .add(new SeparatorProperty(
                            "accessories/plugins/AutomaticLayout.properties_PatternSeparatorName"));
            controls.add(new StylePatternListProperty("level",
                    AUTOMATIC_FORMAT_LEVEL, pTextTranslator,
                    modeController));
            return controls;
        }
    }

    private static Patterns patterns = null;

    /**
     * 
     */
    public AutomaticLayout() {
        super();

    }

    private void setStyle(MindMapNode node) {
        logger.finest("updating node id="
                + node.getObjectId(getMindMapController()) + " and text:"
                + node);
        int depth = depth(node);
        logger.finest("COLOR, depth=" + (depth));
        reloadPatterns();
        int myIndex = patterns.sizeChoiceList() - 1;
        if (depth < patterns.sizeChoiceList())
            myIndex = depth;
        Pattern p = (Pattern) patterns.getChoice(myIndex);
        getMindMapController().applyPattern(node, p);
    }

    private int depth(MindMapNode node) {
        if (node.isRoot())
            return 0;
        return depth(node.getParentNode()) + 1;
    }

    /*
     * (non-Javadoc)
     * 
     * @see freemind.extensions.PermanentNodeHook#onAddChild(freemind.modes.MindMapNode)
     */
    public void onAddChildren(MindMapNode newChildNode) {
        logger.finest("onAddChildren " + newChildNode);
        super.onAddChild(newChildNode);
        setStyleRecursive(newChildNode);
    }

    /*
     * (non-Javadoc)
     * 
     * @see freemind.extensions.PermanentNodeHook#onUpdateChildrenHook(freemind.modes.MindMapNode)
     */
    public void onUpdateChildrenHook(MindMapNode updatedNode) {
        super.onUpdateChildrenHook(updatedNode);
        setStyleRecursive(updatedNode);
    }

    /*
     * (non-Javadoc)
     * 
     * @see freemind.extensions.PermanentNodeHook#onUpdateNodeHook()
     */
    public void onUpdateNodeHook() {
        super.onUpdateNodeHook();
        setStyle(getNode());
    }

    /*
     * (non-Javadoc)
     * 
     * @see freemind.extensions.NodeHook#invoke(freemind.modes.MindMapNode)
     */
    public void invoke(MindMapNode node) {
        super.invoke(node);
        setStyleRecursive(node);
    }

    /** get styles from preferences: */
    private void reloadPatterns() {
        if (patterns == null) {
            String property = getMindMapController().getFrame()
                    .getProperty(AUTOMATIC_FORMAT_LEVEL);
            patterns = StylePatternFactory
                    .getPatternsFromString(property);
        }
    }

    /**
     */
    private void setStyleRecursive(MindMapNode node) {
        logger.finest("setStyle " + node);
        setStyle(node);
        // recurse:
        for (Iterator i = node.childrenUnfolded(); i.hasNext();) {
            MindMapNode child = (MindMapNode) i.next();
            invoke(child);
        }
    }

}
